package com.java.bytebuddy;

import net.bytebuddy.ByteBuddy;
import net.bytebuddy.agent.ByteBuddyAgent;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.dynamic.loading.ClassReloadingStrategy;
import net.bytebuddy.implementation.FixedValue;
import net.bytebuddy.implementation.MethodDelegation;
import net.bytebuddy.matcher.ElementMatchers;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Modifier;

/**
 * @author dushougudu
 */
public class CreateClassDemo {
    public void createNewClassWithStaticMethod() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        DynamicType.Unloaded unloaded = new ByteBuddy()
                .subclass(Object.class)
                .name("Demo")
                .defineMethod("fakeStatic", String.class, Modifier.PUBLIC + Modifier.STATIC)
                .withParameter(String.class, "arg")
                .intercept(MethodDelegation.to(FakeClass.class))
                .make();
        Object o = unloaded.load(getClass().getClassLoader()).getLoaded().newInstance();
        System.out.println(o.getClass().getMethod("fakeStatic", String.class).invoke(o, "fakeStatic"));
    }

    public void createNewClassWithMethod() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        DynamicType.Unloaded unloaded = new ByteBuddy()
                .subclass(Object.class)
                .name("Demo")
                .defineMethod("fakeMethod", String.class, Modifier.PUBLIC + Modifier.STATIC)
                .withParameter(String.class, "arg")
                .intercept(MethodDelegation.to(new FakeClass()))
                .make();
        Object o = unloaded.load(getClass().getClassLoader()).getLoaded().newInstance();
        System.out.println(o.getClass().getMethod("fakeMethod", String.class).invoke(o, "fakeMethod"));
    }

    public void createFromClass() throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        DynamicType.Unloaded unloaded = new ByteBuddy()
                .subclass(FakeClass.class)
                .name("FakeTest")
                .method(ElementMatchers.hasMethodName("fakeMethod"))
                .intercept(MethodDelegation.to(MyInterceptor.class))
                .make();
        Object o = unloaded.load(getClass().getClassLoader()).getLoaded().newInstance();
        System.out.println(o.getClass().getMethod("fakeMethod", String.class).invoke(o, "fakeMethod"));
    }

    public void createFromClassWithAnnotation() throws Exception {
        DynamicType.Unloaded unloaded = new ByteBuddy()
                .subclass(FakeClass.class)
                .name("FakeTest1")
                .method(ElementMatchers.hasMethodName("fakeMethod"))
                .intercept(MethodDelegation.to(MyInterceptor.class))
//                .annotateMethod(AnnotationDescription.Builder.ofType(ApiAnnotation.class).define("author", "dushougudu").build())
                .make();
        for (Annotation annotation : unloaded.load(getClass().getClassLoader()).getLoaded().getAnnotations()) {
            System.out.println(annotation.toString());
        }
    }

    public static void redefineClass() throws Exception {
        ByteBuddyAgent.install();
        new ByteBuddy().redefine(FakeClass.class)
                .method(ElementMatchers.named("fakeMethod"))
                .intercept(FixedValue.value("拦截操作"))
                .make()
                .load(Thread.currentThread().getContextClassLoader(), ClassReloadingStrategy.fromInstalledAgent());

    }


    public static void main(String[] args) throws Exception {
//        CreateClassDemo demo = new CreateClassDemo();
//        demo.createNewClassWithStaticMethod();
//        demo.createNewClassWithMethod();
//        demo.createFromClass();
//        demo.createFromClassWithAnnotation();


//        FakeClass fakeClass = new FakeClass();
//        System.out.println("fakeClass=>" + fakeClass.fakeMethod("XX"));
//
//        Instrumentation instrumentation = ByteBuddyAgent.install();
//        new AgentBuilder.Default().with(AgentBuilder.RedefinitionStrategy.RETRANSFORMATION)
//                .type(ElementMatchers.is(FakeClass.class))
//                .transform((builder, typeDescription, classLoader, javaModule) -> builder
//                        .method(ElementMatchers.named("fakeMethod"))
//                        .intercept(MethodDelegation.to(MyInterceptor.class)))
//                .with(new AgentBuilder.Listener() {
//
//                    @Override
//                    public void onDiscovery(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
////                System.out.println("onDiscovery=>" + typeName);
//                    }
//
//                    @Override
//                    public void onTransformation(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded, DynamicType dynamicType) {
//                        System.out.println("onTransformation=>" + typeDescription);
//                    }
//
//                    @Override
//                    public void onIgnored(TypeDescription typeDescription, ClassLoader classLoader, JavaModule module, boolean loaded) {
////                System.out.println("onIgnored=>" + typeDescription);
//                    }
//
//                    @Override
//                    public void onError(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded, Throwable throwable) {
//                        System.out.println("onError=>" + typeName);
//                    }
//
//                    @Override
//                    public void onComplete(String typeName, ClassLoader classLoader, JavaModule module, boolean loaded) {
////                System.out.println("onComplete=>" + typeName);
//                    }
//
//                }).installOnByteBuddyAgent();
//
//        FakeClass fakeClass1 = new FakeClass();
//        System.out.println("fakeClass1=>" + fakeClass1.fakeMethod("ZZ"));
//        System.out.println("fakeClass=>" + fakeClass.fakeMethod("YY"));
    }
}
